/** @brief File containing the implementation of the basic diamond grid.
 *
 * This file contains the implementation of a grid visualisation where the
 * cells are diamonds.
 *
 * @file HexagonRepresentation.hpp
 * @author Johan "Solo" Luisier
 * @date 2014/06/10
 */
#include "DiamondRepresentation.hpp"

#include "Core/GridBase.hpp"

#include <QtCore/QDebug>
#include <QtCore/qmath.h>
#include <QtCore/QPoint>
#include <QtCore/QPointF>

#include <QtSvg/QGraphicsSvgItem>
#include <QtSvg/QSvgRenderer>

const qreal DiamondRepresentation::TileWidth( 30. );
const qreal DiamondRepresentation::TileHeight( 20. );


DiamondRepresentation::DiamondRepresentation( GridBase* grid, QObject* parent )
    : RepresentationBase( grid, parent )
    , TopLeftPosition( 0., 0. )
    , BottomRightPosition( 0., 0. )
{
    setupTiles();
}

DiamondRepresentation::~DiamondRepresentation()
{}

void DiamondRepresentation::retrieveCell( const QPointF& pt )
{
    /**
     * The used method is the following: first "virtual" rectangular tiles are
     * used to roughly locate the touched area. Those virtual tiles are chosen
     * to overlap the diamond tileand a fourth of the four neigbouring tiles
     * (see here below, the figure "Mechanism of tile identification"). The
     * horizontal position of the virtual tile depends on
     * the row number, which is deduced from the vertical position.
     */

    QPoint pseudoPos( qFloor( pt.x() ) / tileWidth(),
                      qFloor( pt.y() ) / tileHeight() );

    /**
      * The position of the clicked point is then determined with respect to the
      * centre of the rectangle.
      */
    QPointF residual( pt );
    residual.rx() -= tileWidth() * pseudoPos.x() + tileWidth() / 2;
    residual.ry() -= tileHeight() * pseudoPos.y() + tileHeight() / 2;

    /**
      * The cross products of the residual position with the two vectors
      * originating from the rectangle center and heading to the two upper
      * corners are computed.
      *
      * The relative sign of these two products allows to identify if the
      * clicked point lies in the upper/lower or left/right tile. Then the sign
      * of the appropriate residual coordinate gives the exact tile.
      */
    qreal prodA( - tileWidth() * residual.y() + tileHeight() * residual.x() ),
            prodB( tileWidth() * residual.y() + tileHeight() * residual.x() );

    QPoint tileCoord;

    if ( prodA * prodB < 0 )
    {
        if ( residual.y() < 0 )
            tileCoord = QPoint( pseudoPos.x(), 2 * pseudoPos.y() );
        else
            tileCoord = QPoint( pseudoPos.x(), 2 * pseudoPos.y() + 2 );
    }
    else
    {
        if ( residual.x() < 0 )
            tileCoord = QPoint( pseudoPos.x(), 2 * pseudoPos.y() + 1 );
        else
            tileCoord = QPoint( pseudoPos.x() + 1, 2 * pseudoPos.y() + 1 );
    }

    qDebug() << "Clicked on tile at" << tileCoord;

    emit touchedCell( tileCoord );
}

void DiamondRepresentation::setupTiles()
{
    /**
     * Instead of using a complicated logic to know which Cell instance have to
     * be connected to which (question is relevant for the edges only), it is
     * made use of the logic put in addNeighbour: if the added neighbour
     * doesn't exist, (i.e. the pointer is null), then no neighbour is added.
     *
     * The computed @f$ y @f$ position of the hexagon must take into account
     * the fact that it overlaps with the one from the previous row.
     */

    quint32 i, j;

    QSvgRenderer* renderer = new QSvgRenderer( QString( ":/tiles" ) );

    for ( j = 0u; j < GridModel -> height(); j++ )
    {
        for ( i = 0u; i < GridModel -> width(); i++ )
        {
            QGraphicsSvgItem* tile = new QGraphicsSvgItem();
            tile -> setSharedRenderer( renderer );
            tile -> setElementId( QString( "BlueDiamond" ) );
            if ( j % 2u == 0u )
                tile -> setPos( i * tileWidth(),
                                j  * tileHeight() / 2 - tileHeight() / 2  );
            else
                tile -> setPos( i * tileWidth() - tileWidth() / 2,
                                j * tileHeight() / 2 - tileHeight() / 2 );
            tile -> hide();
            addItem( tile );
            CellTiles.append( tile );
        }
    }

    /**
     * The position of the bottom right corner depends on the parity of the
     * number of lines and columns.
     */
    if ( GridModel -> height() % 2u == 0u )
        BottomRightPosition.ry() += tileHeight() * GridModel -> height() / 2u
                                    - tileHeight() / 2u;
    else
        BottomRightPosition.ry() += tileHeight() * GridModel -> height() / 2u
                                    + tileHeight() / 2u;

    if ( GridModel -> width() % 2u == 0u )
        BottomRightPosition.rx() += tileWidth() * GridModel -> width()
                                    - tileWidth() / 2u;
    else
        BottomRightPosition.rx() += tileWidth() * GridModel -> width();

}
